//! Expression parser for NRC language

use super::type_parser::parse_type;
use super::Parser;
use crate::ast::expr::*;
use crate::ast::types::*;
use crate::error::{CompilerError, Result};
use crate::lexer::token::Token;

// Re-export all parsing functions
pub use control_flow::*;
pub use literals::*;
pub use patterns::*;
pub use primary::parse_primary;

mod control_flow;
mod literals;
mod patterns;
mod primary;

/// Parse an expression
pub fn parse_expression(parser: &mut Parser) -> Result<Option<Expression>> {
    parse_assignment(parser)
}

/// Parse assignment expression (lowest precedence)
fn parse_assignment(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_conditional(parser)?;

    if let Some(expr) = &mut expr {
        // Note: Token::Assign is handled at statement level, not as binary operator
        while let Some(operator) = parser.consume_any(&[
            Token::PlusAssign,
            Token::MinusAssign,
            Token::StarAssign,
            Token::SlashAssign,
            Token::PercentAssign,
            Token::AmpersandAssign,
            Token::PipeAssign,
            Token::CaretAssign,
            Token::LeftShiftAssign,
            Token::RightShiftAssign,
        ])? {
            let right = parse_conditional(parser)?;
            if let Some(right) = right {
                let binary_op = match operator {
                    Token::PlusAssign => BinaryOp::AddAssign,
                    Token::MinusAssign => BinaryOp::SubAssign,
                    Token::StarAssign => BinaryOp::MulAssign,
                    Token::SlashAssign => BinaryOp::DivAssign,
                    Token::PercentAssign => BinaryOp::ModAssign,
                    Token::AmpersandAssign => BinaryOp::BitAndAssign,
                    Token::PipeAssign => BinaryOp::BitOrAssign,
                    Token::CaretAssign => BinaryOp::BitXorAssign,
                    Token::LeftShiftAssign => BinaryOp::LeftShiftAssign,
                    Token::RightShiftAssign => BinaryOp::RightShiftAssign,
                    _ => unreachable!(),
                };

                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: binary_op,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

pub(super) fn parse_struct_literal_expression(
    parser: &mut Parser,
    struct_type: Type,
) -> Result<Expression> {
    let location = parser.current_location();
    parser.expect(&Token::LeftBrace)?;
    let fields = parse_struct_field_initializers(parser)?;
    parser.expect(&Token::RightBrace)?;
    Ok(Expression::Struct(StructExpr {
        struct_type,
        fields,
        location,
    }))
}

fn parse_struct_field_initializers(parser: &mut Parser) -> Result<Vec<FieldInit>> {
    let mut fields = Vec::new();

    if !parser.check(&Token::RightBrace) {
        loop {
            if let Some(Token::Identifier(field_name)) = parser.peek().map(|t| &t.token) {
                let name = field_name.clone();
                parser.advance()?;
                parser.expect(&Token::Colon)?;

                let value = parse_expression(parser)?;
                if let Some(value) = value {
                    fields.push(FieldInit {
                        name,
                        value,
                        location: parser.current_location(),
                    });
                }
            } else {
                return Err(CompilerError::syntax(
                    parser.current_location().line,
                    parser.current_location().column,
                    "Expected field name in struct literal",
                ));
            }

            if !parser.consume(&Token::Comma)? {
                break;
            }

            if parser.check(&Token::RightBrace) {
                break;
            }
        }
    }

    Ok(fields)
}

/// Parse conditional expression (ternary operator)
fn parse_conditional(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_logical_or(parser)?;

    if let Some(expr) = &mut expr {
        if parser.consume(&Token::Question)? {
            let true_expr = parse_expression(parser)?;
            parser.expect(&Token::Colon)?;
            let false_expr = parse_expression(parser)?;

            if let (Some(true_expr), Some(false_expr)) = (true_expr, false_expr) {
                *expr = Expression::Conditional(ConditionalExpr {
                    condition: Box::new(expr.clone()),
                    true_expr: Box::new(true_expr),
                    false_expr: Box::new(false_expr),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse logical OR expression
fn parse_logical_or(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_logical_and(parser)?;

    if let Some(expr) = &mut expr {
        while parser.consume(&Token::LogicalOr)? {
            let right = parse_logical_and(parser)?;
            if let Some(right) = right {
                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: BinaryOp::LogicalOr,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse logical AND expression
fn parse_logical_and(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_equality(parser)?;

    if let Some(expr) = &mut expr {
        while parser.consume(&Token::LogicalAnd)? {
            let right = parse_equality(parser)?;
            if let Some(right) = right {
                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: BinaryOp::LogicalAnd,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse equality expression
fn parse_equality(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_comparison(parser)?;

    if let Some(expr) = &mut expr {
        while let Some(operator) = parser.consume_any(&[Token::Equal, Token::NotEqual])? {
            let right = parse_comparison(parser)?;
            if let Some(right) = right {
                let binary_op = match operator {
                    Token::Equal => BinaryOp::Equal,
                    Token::NotEqual => BinaryOp::NotEqual,
                    _ => unreachable!(),
                };

                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: binary_op,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse comparison expression
pub fn parse_comparison(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_bitwise_or(parser)?;

    if let Some(expr) = &mut expr {
        while let Some(operator) = parser.consume_any(&[
            Token::Greater,
            Token::GreaterEqual,
            Token::Less,
            Token::LessEqual,
        ])? {
            let right = parse_bitwise_or(parser)?;
            if let Some(right) = right {
                let binary_op = match operator {
                    Token::Greater => BinaryOp::Greater,
                    Token::GreaterEqual => BinaryOp::GreaterEqual,
                    Token::Less => BinaryOp::Less,
                    Token::LessEqual => BinaryOp::LessEqual,
                    _ => unreachable!(),
                };

                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: binary_op,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse bitwise OR expression
fn parse_bitwise_or(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_bitwise_xor(parser)?;

    if let Some(expr) = &mut expr {
        while parser.consume(&Token::Pipe)? {
            let right = parse_bitwise_xor(parser)?;
            if let Some(right) = right {
                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: BinaryOp::BitOr,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse bitwise XOR expression
fn parse_bitwise_xor(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_bitwise_and(parser)?;

    if let Some(expr) = &mut expr {
        while parser.consume(&Token::Caret)? {
            let right = parse_bitwise_and(parser)?;
            if let Some(right) = right {
                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: BinaryOp::BitXor,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse bitwise AND expression
fn parse_bitwise_and(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_shift(parser)?;

    if let Some(expr) = &mut expr {
        while parser.consume(&Token::Ampersand)? {
            let right = parse_shift(parser)?;
            if let Some(right) = right {
                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: BinaryOp::BitAnd,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse shift expression
fn parse_shift(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_addition(parser)?;

    if let Some(expr) = &mut expr {
        while let Some(operator) = parser.consume_any(&[Token::LeftShift, Token::RightShift])? {
            let right = parse_addition(parser)?;
            if let Some(right) = right {
                let binary_op = match operator {
                    Token::LeftShift => BinaryOp::LeftShift,
                    Token::RightShift => BinaryOp::RightShift,
                    _ => unreachable!(),
                };

                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: binary_op,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse addition expression
fn parse_addition(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_multiplication(parser)?;

    if let Some(expr) = &mut expr {
        while let Some(operator) = parser.consume_any(&[Token::Plus, Token::Minus])? {
            let right = parse_multiplication(parser)?;
            if let Some(right) = right {
                let binary_op = match operator {
                    Token::Plus => BinaryOp::Add,
                    Token::Minus => BinaryOp::Sub,
                    _ => unreachable!(),
                };

                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: binary_op,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse multiplication expression
fn parse_multiplication(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_unary(parser)?;

    if let Some(expr) = &mut expr {
        while let Some(operator) =
            parser.consume_any(&[Token::Star, Token::Slash, Token::Percent])?
        {
            let right = parse_unary(parser)?;
            if let Some(right) = right {
                let binary_op = match operator {
                    Token::Star => BinaryOp::Mul,
                    Token::Slash => BinaryOp::Div,
                    Token::Percent => BinaryOp::Mod,
                    _ => unreachable!(),
                };

                *expr = Expression::Binary(BinaryExpr {
                    left: Box::new(expr.clone()),
                    operator: binary_op,
                    right: Box::new(right),
                    location: parser.current_location(),
                });
            }
        }
    }

    Ok(expr)
}

/// Parse unary expression
fn parse_unary(parser: &mut Parser) -> Result<Option<Expression>> {
    // Special handling for &Type{} syntax (pointer creation from literal)
    // or &expression syntax (convert value to pointer)
    if parser.check(&Token::Ampersand) {
        let loc = parser.current_location();

        // Lookahead to distinguish &Type{} from &expression
        // We need to check if this looks like &Type{value} pattern
        // by looking ahead for the { token
        let looks_like_type_literal = (|| {
            // Save position and try to peek ahead
            let next_token = parser.peek_ahead(1);

            // Check if next token could be start of a type
            let could_be_type = match next_token.map(|t| &t.token) {
                Some(Token::Identifier(_)) | Some(Token::Star) | Some(Token::LeftBracket) => true,
                _ => false,
            };

            if !could_be_type {
                return false;
            }

            // Try to find a { within the next few tokens
            // This is a heuristic: if we see { within 3 tokens, assume it's &Type{}
            for i in 2..5 {
                if let Some(tok) = parser.peek_ahead(i) {
                    if matches!(tok.token, Token::LeftBrace) {
                        return true;
                    }
                    // Stop looking if we hit something that can't be part of a type
                    if matches!(
                        tok.token,
                        Token::Semicolon
                            | Token::Comma
                            | Token::RightParen
                            | Token::RightBracket
                            | Token::RightBrace
                    ) {
                        break;
                    }
                }
            }
            false
        })();

        if looks_like_type_literal {
            // Parse as &Type{} syntax
            parser.advance()?; // consume &

            // Try to parse type for &Type{} syntax
            let ty = super::type_parser::parse_type(parser)?;
            if let Some(ty) = ty {
                // Check if followed by {} for initialization
                if !parser.check(&Token::LeftBrace) {
                    // Type parsed but not followed by {}, this is an error
                    return Err(CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected '{' after type in &Type{} syntax",
                    ));
                }

                // Check if this is a basic type by examining the Type itself
                let is_basic_type = matches!(ty, Type::Basic(_));
                let next_is_right_brace = parser
                    .peek_ahead(1)
                    .map_or(false, |t| matches!(t.token, Token::RightBrace));

                if is_basic_type && !next_is_right_brace {
                    // Basic type: &int32{100}, &float64{3.14}, etc.
                    parser.expect(&Token::LeftBrace)?;
                    // Parse single value expression
                    let value = parse_expression(parser)?;
                    parser.expect(&Token::RightBrace)?;

                    if let Some(value) = value {
                        // Create a special struct expression with single unnamed field
                        let struct_init = Expression::Struct(StructExpr {
                            struct_type: ty.clone(),
                            fields: vec![FieldInit {
                                name: String::from("__value__"), // Special marker for basic type
                                value,
                                location: loc,
                            }],
                            location: loc,
                        });

                        return Ok(Some(Expression::Unary(UnaryExpr {
                            operator: UnaryOp::AddrOf,
                            operand: Box::new(struct_init),
                            location: loc,
                        })));
                    }
                } else {
                    // Struct type or empty initializer for basic type
                    let struct_init = parse_struct_literal_expression(parser, ty.clone())?;

                    // Wrap in Unary AddrOf to indicate pointer creation
                    return Ok(Some(Expression::Unary(UnaryExpr {
                        operator: UnaryOp::AddrOf,
                        operand: Box::new(struct_init),
                        location: loc,
                    })));
                }
            } else {
                // Could not parse as type after we determined it looks like &Type{}
                // This shouldn't happen with good lookahead, but handle it anyway
                return Err(CompilerError::syntax(
                    loc.line,
                    loc.column,
                    "Expected type after '&' in &Type{} syntax",
                ));
            }
        } else {
            // Doesn't look like &Type{}, treat as &expression (address-of operator)
            parser.advance()?; // consume &

            let operand = parse_unary(parser)?;
            if let Some(operand) = operand {
                return Ok(Some(Expression::Unary(UnaryExpr {
                    operator: UnaryOp::AddrOf,
                    operand: Box::new(operand),
                    location: loc,
                })));
            } else {
                return Err(CompilerError::syntax(
                    loc.line,
                    loc.column,
                    "Expected expression after '&'",
                ));
            }
        }
    }

    if let Some(operator) = parser.consume_any(&[
        Token::Not,
        Token::Tilde,
        Token::Minus,
        Token::Plus,
        Token::Star,
    ])? {
        let operand = parse_unary(parser)?;
        if let Some(operand) = operand {
            let unary_op = match operator {
                Token::Not => UnaryOp::Not,
                Token::Tilde => UnaryOp::BitNot,
                Token::Minus => UnaryOp::Neg,
                Token::Plus => UnaryOp::Pos,
                Token::Star => UnaryOp::Deref,
                _ => unreachable!(),
            };

            return Ok(Some(Expression::Unary(UnaryExpr {
                operator: unary_op,
                operand: Box::new(operand),
                location: parser.current_location(),
            })));
        }
    }

    parse_postfix(parser)
}

/// Parse postfix expression
fn parse_postfix(parser: &mut Parser) -> Result<Option<Expression>> {
    let mut expr = parse_primary(parser)?;

    while let Some(expr) = &mut expr {
        if parser.check(&Token::Question) {
            if parser.question_starts_conditional()? {
                break;
            }
            let location = parser.current_location();
            parser.advance()?; // consume '?'
            *expr = Expression::Try(TryExpr {
                expression: Box::new(expr.clone()),
                location,
            });
            continue;
        } else if parser.check(&Token::Not) {
            let location = parser.current_location();
            parser.advance()?; // consume '!'
            *expr = Expression::ForceUnwrap(ForceUnwrapExpr {
                expression: Box::new(expr.clone()),
                location,
            });
            continue;
        } else if parser.consume(&Token::ColonColon)? {
            // Method call or static method call (e.g., Person::new)
            let method_name = match parser.peek().map(|t| &t.token) {
                Some(Token::Identifier(name)) => {
                    let name = name.clone();
                    parser.advance()?;
                    name
                }
                Some(Token::New) => {
                    parser.advance()?;
                    "new".to_string()
                }
                _ => {
                    return Err(CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected method name after '::'",
                    ));
                }
            };

            let method_type_args = parse_type_arguments_for_invocation(parser)?;

            // Parse method call arguments
            if parser.consume(&Token::LeftParen)? {
                let mut arguments = Vec::new();

                if !parser.check(&Token::RightParen) {
                    loop {
                        if let Some(arg) = parse_expression(parser)? {
                            arguments.push(arg);
                        }

                        if !parser.consume(&Token::Comma)? {
                            break;
                        }
                    }
                }

                parser.expect(&Token::RightParen)?;

                *expr = Expression::Call(CallExpr {
                    callee: Box::new(Expression::MethodCall(MethodCallExpr {
                        object: Box::new(expr.clone()),
                        method: method_name,
                        type_args: method_type_args.clone(),
                        arguments: arguments.clone(),
                        location: parser.current_location(),
                    })),
                    type_args: Vec::new(),
                    arguments,
                    resolved: None,
                    location: parser.current_location(),
                });
            } else {
                // Just a method reference without call
                *expr = Expression::MethodCall(MethodCallExpr {
                    object: Box::new(expr.clone()),
                    method: method_name,
                    type_args: vec![], // TODO: Parse type arguments
                    arguments: vec![], // No arguments for method reference
                    location: parser.current_location(),
                });
            }
        } else if parser.check(&Token::Dot) {
            // Check for dot before checking for left paren
            // This ensures method calls (object.method()) are parsed correctly
            parser.advance()?; // consume '.'

            // Try to get field/method name - can be identifier or certain keywords
            let field_name = match parser.peek().map(|t| &t.token) {
                Some(Token::Identifier(name)) => {
                    let name = name.clone();
                    parser.advance()?;
                    name
                }
                _ => {
                    return Err(CompilerError::syntax(
                        parser.current_location().line,
                        parser.current_location().column,
                        "Expected field or method name after '.'",
                    ));
                }
            };

            if !field_name.is_empty() {
                let method_type_args = parse_type_arguments_for_invocation(parser)?;
                if parser.check(&Token::LeftParen) {
                    // This is a method call: object.method(...)
                    let mut arguments = Vec::new();

                    parser.expect(&Token::LeftParen)?;

                    if !parser.check(&Token::RightParen) {
                        loop {
                            if let Some(arg) = parse_expression(parser)? {
                                arguments.push(arg);
                            }

                            if !parser.consume(&Token::Comma)? {
                                break;
                            }
                        }
                    }

                    parser.expect(&Token::RightParen)?;

                    *expr = Expression::MethodCall(MethodCallExpr {
                        object: Box::new(expr.clone()),
                        method: field_name,
                        type_args: method_type_args,
                        arguments,
                        location: parser.current_location(),
                    });
                } else {
                    // This is a field access: object.field
                    *expr = Expression::FieldAccess(FieldAccessExpr {
                        object: Box::new(expr.clone()),
                        field: field_name,
                        location: parser.current_location(),
                    });
                }
            }
        } else if parser.check(&Token::LeftParen) || parser.check(&Token::Less) {
            let call_type_args = parse_type_arguments_for_invocation(parser)?;
            if !parser.check(&Token::LeftParen) {
                break;
            }

            parser.expect(&Token::LeftParen)?; // consume '('

            let mut arguments = Vec::new();

            if !parser.check(&Token::RightParen) {
                loop {
                    if let Some(arg) = parse_expression(parser)? {
                        arguments.push(arg);
                    }

                    if !parser.consume(&Token::Comma)? {
                        break;
                    }
                }
            }

            parser.expect(&Token::RightParen)?;

            if let Expression::FieldAccess(field_access) = expr.clone() {
                *expr = Expression::MethodCall(MethodCallExpr {
                    object: field_access.object,
                    method: field_access.field,
                    type_args: call_type_args,
                    arguments,
                    location: parser.current_location(),
                });
            } else {
                *expr = Expression::Call(CallExpr {
                    callee: Box::new(expr.clone()),
                    type_args: call_type_args,
                    arguments,
                    resolved: None,
                    location: parser.current_location(),
                });
            }
        } else if parser.consume(&Token::LeftBracket)? {
            // Check if this is slice range or index access
            // Slice range: [start:end], [:end], [start:], [:]
            // Index access: [index]

            // Try to parse first expression (could be start index or the only index)
            let first_expr = if parser.check(&Token::Colon) {
                // [:end] or [:] - no start index
                None
            } else {
                parse_expression(parser)?
            };

            if parser.consume(&Token::Colon)? {
                // This is a slice range operation
                let end_expr = if parser.check(&Token::RightBracket) {
                    // [start:] or [:] - no end index
                    None
                } else {
                    parse_expression(parser)?
                };

                parser.expect(&Token::RightBracket)?;

                *expr = Expression::SliceRange(SliceRangeExpr {
                    object: Box::new(expr.clone()),
                    start: first_expr.map(Box::new),
                    end: end_expr.map(Box::new),
                    location: parser.current_location(),
                });
            } else {
                // This is a regular index access
                parser.expect(&Token::RightBracket)?;

                if let Some(index) = first_expr {
                    *expr = Expression::IndexAccess(IndexAccessExpr {
                        object: Box::new(expr.clone()),
                        index: Box::new(index),
                        location: parser.current_location(),
                    });
                }
            }
        } else {
            break;
        }
    }

    Ok(expr)
}

/// Attempt to parse `<T, U>` style type arguments that must be immediately followed by `(` to form a call.
fn parse_type_arguments_for_invocation(parser: &mut Parser) -> Result<Vec<Type>> {
    if !parser.check(&Token::Less) {
        return Ok(Vec::new());
    }

    let saved_current = parser.current.clone();
    let saved_buffer = parser.buffer.clone();

    parser.advance()?; // consume '<'
    let mut type_args = Vec::new();

    if !parser.check(&Token::Greater) {
        loop {
            if let Some(arg) = parse_type(parser)? {
                type_args.push(arg);
            } else {
                parser.current = saved_current.clone();
                parser.buffer = saved_buffer.clone();
                return Ok(Vec::new());
            }

            if !parser.consume(&Token::Comma)? {
                break;
            }
        }
    }

    if !parser.check(&Token::Greater) {
        parser.current = saved_current.clone();
        parser.buffer = saved_buffer.clone();
        return Ok(Vec::new());
    }

    parser.advance()?; // consume '>'

    if !parser.check(&Token::LeftParen) {
        parser.current = saved_current;
        parser.buffer = saved_buffer;
        return Ok(Vec::new());
    }

    Ok(type_args)
}
